﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Collections.ObjectModel;
using Renci.iRods.Common;

namespace Renci.iRods.Data
{
    public class MetadataCollection : ICollection<MetaData>, IEnumerable<MetaData>, ICollection, IEnumerable
    {
        private IRodsConnection _connection;
        private string _fullName;
        private string _type;
        private ICollection<MetaData> _metadataCollection = new Collection<MetaData>();

        private ICollection<MetaData> _metadataRemovedItems = new Collection<MetaData>();

        private IRodsQuery _query;

        private Func<IRodsDataReader, MetaData> _projector;

        public MetadataCollection(IRodsConnection connection, string name, string type, IRodsQuery query, Func<IRodsDataReader, MetaData> projector)
        {
            this._connection = connection;
            this._fullName = name;
            this._type = type;
            this._query = query;
            this._projector = projector;
        }

        /// <summary>
        /// Refreshes metadata collection by populating it from the server.
        /// </summary>
        public void Refresh()
        {
            this._metadataCollection.Clear();
            this._metadataRemovedItems.Clear();
            var result = this._connection.ExecuteQuery(this._query);

            while (result.Read())
            {
                this._metadataCollection.Add(this._projector(result));
            }
        }

        public void SaveChanges()
        {
            var dirtyItems = from i in this._metadataCollection
                             where i.IsDirty
                             select i;

            foreach (var item in dirtyItems)
            {
                if (item.ID > 0)
                {
                    //  Update existing item
                    this._connection.ExecuteModifyMetadata("mod", this._type, this._fullName, item.OldName, item.OldValue, string.Format("n:{0}", item.Name), string.Format("v:{0}", item.Value), string.Format("u:{0}", item.Unit));
                }
                else
                {
                    //  Create new item
                    this._connection.ExecuteModifyMetadata("add", this._type, this._fullName, item.Name, item.Value, item.Unit);
                }
            }

            foreach (var item in this._metadataRemovedItems)
            {
                //  Remove items
                this._connection.ExecuteModifyMetadata("rm", this._type, this._fullName, item.Name, item.Value, item.Unit);
            }

            this.Refresh();
        }

        #region ICollection<MetaData> Members

        public void Add(MetaData item)
        {
            this._metadataCollection.Add(item);
        }

        public void Add(string name, string value)
        {
            this.Add(new MetaData(name, value));
        }

        public void Add(string name, string value, string unit)
        {
            this.Add(new MetaData(name, value, unit));
        }

        public void Clear()
        {
            foreach (var item in this._metadataCollection)
            {
                this._metadataRemovedItems.Add(item);
            }
            this._metadataCollection.Clear();
        }

        public bool Contains(MetaData item)
        {
            return this._metadataCollection.Contains(item);
        }

        public void CopyTo(MetaData[] array, int arrayIndex)
        {
            this._metadataCollection.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return this._metadataCollection.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(MetaData item)
        {
            this._metadataRemovedItems.Add(item);

            return this._metadataCollection.Remove(item);
        }

        #endregion

        #region IEnumerable<MetaData> Members

        public IEnumerator<MetaData> GetEnumerator()
        {
            return this._metadataCollection.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this._metadataCollection.GetEnumerator();
        }

        #endregion

        #region ICollection Members

        public void CopyTo(Array array, int index)
        {
            ((ICollection)this).CopyTo(array, index);
        }

        public bool IsSynchronized
        {
            get { return false; }
        }

        public object SyncRoot
        {
            get { return this; }
        }

        #endregion
    }
}
